package manage

import (
	"container/list"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"uds-saas-go-parser/common"
	"uds-saas-go-parser/utils"
)

func AddRawParse(typeName string, parser common.ParserInterface) {

}

func ParseRawData(head *list.List) {
	for e := head.Front(); e != nil; e = e.Next() {

		rd, ok := (e.Value).(common.ParsedRawData)
		if ok {
			fmt.Printf("%v ", rd)
			switch rd.Type {
			case common.GOTO:
				ParseGoto(e)
			case common.CIRCLE:
				ParseCircle(e)
			case common.CYCLE:
				ParseCycle(e)
			case common.PAINT:
				ParseColor(e)
			}
		}
	}
}

var mapColors map[string]common.ColorEvent = map[string]common.ColorEvent{
	"RAPID": common.ColorEvent{Color: 1234},
	"CUT":   common.ColorEvent{Color: 4567},
}

func ParseColor(elm *list.Element) {

	currentPrd, _ := (elm.Value).(common.ParsedRawData)
	var colorName string

	spts := strings.Split(currentPrd.RawData[0], ",")
	colorName = spts[0][len(common.PAINT)+1:]

	if len(currentPrd.RawData) > 1 {
		color, exist := mapColors[colorName+"_G0"]
		if !strings.HasPrefix(currentPrd.RawData[1], common.RAPID) && exist {
			currentPrd.Color = color
		}
		if !exist {
			currentPrd.Color = mapColors[colorName]
		}
	}
	if elm.Prev() != nil {
		prevPrd, _ := (elm.Prev().Value).(common.ParsedRawData)
		currentPrd.ToolAxisDir = prevPrd.ToolAxisDir
	}

	elm.Value = currentPrd

}

func ParseCircle(elm *list.Element) (err error) {
	currentPrd, _ := (elm.Value).(common.ParsedRawData)

	prevPrd, _ := (elm.Prev().Value).(common.ParsedRawData)

	if prevPrd.Type != common.GOTO {
		// 如果前一个点不是GOTO则报错
		err = errors.New("circle prev point must be GOTO")
	}

	points := []common.Point{}

	rawStr := currentPrd.RawData[0]

	preLen := len(common.CIRCLE)

	spts := strings.Split(rawStr[preLen+1:], ",")

	var pts []float64 = make([]float64, 11)
	for i := 0; i < 7; i++ {
		pts[i], _ = strconv.ParseFloat(strings.Trim(spts[i], " "), 64)
	}

	pt_circle_center := common.Point{
		X: pts[0],
		Y: pts[1],
		Z: pts[2],
	}

	pt_circle_orientation := common.Point{
		X: pts[3],
		Y: pts[4],
		Z: pts[5],
	}

	pt_circle_r := common.Point{
		X: pts[6],
	}

	points = append(points, pt_circle_center, pt_circle_orientation, pt_circle_r)

	currentPrd.Data = points

	if elm.Prev() != nil {
		currentPrd.Color = prevPrd.Color
		currentPrd.ToolAxisDir = prevPrd.ToolAxisDir
	}

	elm.Value = currentPrd
	return

}

func ParseGoto(elm *list.Element) {
	prd, _ := (elm.Value).(common.ParsedRawData)
	rawStr := prd.RawData[0]
	pts := ParseGotoStr(rawStr)
	prd.Data = pts[0]
	if len(pts) > 1 {
		prd.ToolAxisDir = pts[1]
	}
	if elm.Prev() != nil {
		prevPrd, _ := (elm.Prev().Value).(common.ParsedRawData)
		prd.ToolAxisDir = prevPrd.ToolAxisDir
		prd.Color = prevPrd.Color
	}
	elm.Value = prd
}

func ParseGotoStr(line string) []common.Point {
	skipLen := len(common.GOTO) + 1
	rawStr := line[skipLen:]
	spts := strings.Split(rawStr, ",")
	res := []common.Point{}
	gx, _ := strconv.ParseFloat(strings.Trim(spts[0], " "), 64)
	gy, _ := strconv.ParseFloat(strings.Trim(spts[1], " "), 64)
	gz, _ := strconv.ParseFloat(strings.Trim(spts[2], " "), 64)
	res = append(res, common.Point{X: gx, Y: gy, Z: gz})
	if len(spts) > 5 {
		ax, _ := strconv.ParseFloat(strings.Trim(spts[3], " "), 64)
		ay, _ := strconv.ParseFloat(strings.Trim(spts[4], " "), 64)
		az, _ := strconv.ParseFloat(strings.Trim(spts[5], " "), 64)
		res = append(res, common.Point{X: ax, Y: ay, Z: az})

	}
	return res
}

func ParseCycleToMap(line string) map[string]float64 {
	res := make(map[string]float64)
	spts := strings.Split(line, ",")
	for i := 1; i < len(spts); i += 2 {
		value, _ := strconv.ParseFloat(strings.Trim(spts[i+1], " "), 64)
		res[spts[i]] = value
	}
	return res
}

func ParseCycle(elm *list.Element) {
	prd, _ := (elm.Value).(common.ParsedRawData)
	stk := utils.NewStack()
	var lstGotos []common.Point
	mapCycle := make(map[string]float64)
	points := []common.Point{}
	var color common.ColorEvent
	for _, item := range prd.RawData {
		if strings.HasPrefix(item, common.CYCLE) {
			mapCycle = ParseCycleToMap(item)
		} else if strings.HasPrefix(item, common.GOTO) {
			pts := ParseGotoStr(item)
			lstGotos = append(lstGotos, pts[0])
		} else if strings.HasPrefix(item, common.PAINT) {
			colorName := item[len(common.PAINT)+1:]
			color = mapColors[colorName]
		}
	}

	prevPrd, _ := (elm.Prev().Value).(common.ParsedRawData)

	for _, pt := range lstGotos {
		rtrcto := common.Point{
			X: pt.X + prevPrd.ToolAxisDir.X*mapCycle[common.RTRCTO],
			Y: pt.Y + prevPrd.ToolAxisDir.Y*mapCycle[common.RTRCTO],
			Z: pt.Z + prevPrd.ToolAxisDir.Z*mapCycle[common.RTRCTO],
		}

		stk.Push(rtrcto)
		points = append(points, rtrcto)
		stk.Push(pt)
		points = append(points, pt)
		fedto := common.Point{
			X: pt.X + prevPrd.ToolAxisDir.X*mapCycle[common.FEDTO],
			Y: pt.Y + prevPrd.ToolAxisDir.Y*mapCycle[common.FEDTO],
			Z: pt.Z + prevPrd.ToolAxisDir.Z*mapCycle[common.FEDTO],
		}
		points = append(points, fedto)

		for !stk.IsEmpty() {
			pti, _ := stk.Pop()
			point, _ := pti.(common.Point)
			points = append(points, point)
		}
	}

	prd.ToolAxisDir = prevPrd.ToolAxisDir
	prd.Color = color
	prd.Data = points
	elm.Value = prd
}

func JointCyclePointsToLines(data []common.Point, color int) ([]common.ParsedData, common.Point, common.Point) {
	res := make([]common.ParsedData, 0)
	LEN := len(data)
	recordLastPoint := data[LEN-1]
	recordFirstPoint := data[0]

	prevPoint := data[0]
	for i := 1; i < len(data); i++ {
		currentPoint := data[i]
		linedata := make([]float64, 6)
		linedata[0] = prevPoint.X
		linedata[1] = prevPoint.Y
		linedata[2] = prevPoint.Z
		linedata[3] = currentPoint.X
		linedata[4] = currentPoint.Y
		linedata[5] = currentPoint.Z

		pd := common.ParsedData{
			Data:  linedata,
			Color: color,
		}

		res = append(res, pd)

		prevPoint = currentPoint
	}

	return res, recordFirstPoint, recordLastPoint
}

func JoinTogether(head *list.List) []common.ParsedData {
	allData := []common.ParsedData{}

	prevPrd, _ := (head.Front().Value).(common.ParsedRawData)

	newHead := head.Front().Next()
	for e := newHead; e != nil; e = e.Next() {

		rd, ok := (e.Value).(common.ParsedRawData)
		if ok {
			switch rd.Type {
			case common.GOTO:
				if prevPrd.Type != common.GOTO {
					prevPrd, _ = (e.Value).(common.ParsedRawData)
					continue
				}
				if prevPt, ok := prevPrd.Data.(common.Point); ok {

					linedata := make([]float64, 6)
					linedata[0] = prevPt.X
					linedata[1] = prevPt.Y
					linedata[2] = prevPt.Z

					if curretPt, _ := rd.Data.(common.Point); ok {
						linedata[3] = curretPt.X
						linedata[4] = curretPt.Y
						linedata[5] = curretPt.Z

						allData = append(allData, common.ParsedData{
							Data:  linedata,
							Color: prevPrd.Color.Color,
						})
					}

				}
			case common.CIRCLE:
				if prevPt, ok := prevPrd.Data.(common.Point); ok {
					if currentPts, ok := rd.Data.([]common.Point); ok {
						if nextrd, ok := (e.Next().Value).(common.ParsedRawData); ok {
							if nextPt, ok := nextrd.Data.(common.Point); ok {
								circlepts := []float64{currentPts[0].X, currentPts[0].Y, currentPts[0].Z}
								axispts := []float64{currentPts[1].X, currentPts[1].Y, currentPts[1].Z}
								circleR := currentPts[2].X
								startpts := []float64{prevPt.X, prevPt.Y, prevPt.Z}
								nextpts := []float64{nextPt.X, nextPt.Y, nextPt.Z}
								allData = append(allData, common.ParsedData{
									Data:  []any{circlepts, axispts, circleR, startpts, nextpts},
									Color: prevPrd.Color.Color,
								})
							}
						}

					}
				}
			case common.CYCLE:
				if pts, ok := rd.Data.([]common.Point); ok {
					pds, firstPt, _ := JointCyclePointsToLines(pts, rd.Color.Color)

					if prevPrd.Type == common.GOTO {
						prevPt, _ := (prevPrd.Data).(common.Point)
						linedata := make([]float64, 6)
						linedata[0] = prevPt.X
						linedata[1] = prevPt.Y
						linedata[2] = prevPt.Z
						linedata[3] = firstPt.X
						linedata[4] = firstPt.Y
						linedata[5] = firstPt.Z

						pd := common.ParsedData{
							Data:  linedata,
							Color: prevPrd.Color.Color,
						}

						allData = append(allData, pd)
					}
					allData = append(allData, pds...)

				}
			}

			prevPrd, _ = (e.Value).(common.ParsedRawData)
		}
	}

	return allData
}
